16.1 结构变量
结构:结构的特性与数组很不相同。
- 结构的元素(成员)可能具有不同的类型
- 每个结构成员都有名字
- 为了选择特殊的结构成员需要知名结构成员的名字而不是它的位置
扩展:大多数语言都提供类似的特性,所以结构可能听起来很舒需。再其它语言中,经常把结构称为纪录(record),把结构的成员称为字段(field)。
16.1.1 结构变量的声明
语法:只声明不初始化(会非配内存但成员不会初始化)
1 | struct{ |
实例:结构实例化的变量具备以下特点
- 成员在内存中是按照顺序存储的
- 内部成员拥有单独的名字空间(name space)
1 | //零件 |
16.1.2 结构变量的初始化
语法:声明的同时初始化
1 | struct{ |
规则:类似数组
- 用于结构初始化式的表达式必须是常量
- 初始化式可以短于它所初始化的结构,任何剩余的成员都用0作为它的初始值
1 | struct{ |
16.1.3 对结构的操作
限制:不能用==
或!=
判定两个结构是否相等或不等。
16.1.3.1 访问成员
左值:结构成员的值是左值
- 可以出现在赋值运算的左侧
- 作为自增或自减表达式的操作数
1 | printf("Part number %d:", part1.number); |
逗号运算符:结构变量.成员名
- 优先级和后缀++和后缀–相同(几乎高于所有其他运算符)
1 | scanf("%d", &part1.on_hand);//.运算符优先级高于& |
16.1.3.2 赋值运算
说明:数组不能用=运算符实现变量间数组内容的复制,但结构变量可以。
注意:只能用于同一个结构类型声明的的变量之间。
技巧:把需要复制的数组嵌在结构体内(作为成员)进行复制。
1 | struct{ |
16.2 结构类型
说明:上一小结重点放在结构变量而不是结构类型本身上,这一节将重点观察结构类型。
命名结构类型:如果需要在程序的不同位置声明结构变量,上一节的“匿名结构”就行不通了。c语言提供了两种命名结构的方法
- 声明“结构标记”(结构用语链表时,只能声明“结构标记”)
- 使用
typedef
定义类型名
16.2.1 结构标记的声明
结构标记(structure tag):结构标记用于标记某种特定结构类型的名字。
1 | struct 结构类型名{ |
1 | //不仅声明了标记part,而且声明了变量 |
16.2.2 结构类型的定义
说明:用typedef来定义真正的类型名。
1 | typedef struct{ |
1 | typedef struct{ |
16.2.3 结构类型的实际参数和返回值
缺点:带来一定系统开销,尤其是结构题很大的时候。给函数传递结构和从函数返回结构都要求使用结构中所有成员的副本。
技巧:有时用指向结构的指针来代替传递给函数(或函数返回)的结构本身是很明智的做法。
16.2.3.1 用作参数
1 | // 定义 |
16.2.3.2 用作返回值
1 | // 定义 |
16.3 数组和结构的嵌套
16.3.1 嵌套的结构
1 | // 定义姓名 |
16.3.2 结构数组
说明:结构可以作为数组的元素。
1 | /*声明*/ |
16.3.3 结构数组的初始化
语法:类似二维数组的初始化,每个结构都拥有自己的大括号。
注意:每个结构值的内层大括号是可选项。
1 | // 定义结构:国家代码 |
16.3.4 程序:维护零件数据库
16.3.4.1 编写
readline.h
1 |
|
readline.c
1 |
|
invent.c
1 | /** |
16.3.4.2 编译
$ vim makefile
1 | invent:invent.o readline.o |
$ make
16.3.4.3 运行
1 | $ ./invent |
16.4 联合
特点(和结构相比)
相同点
- 包含一个或多个成员
- 成员可以是不同的类型
- 声明标记和类型的方式
- 访问成员的方式
- 可以使用
=
进行复制操作 - 可以在函数间传递或作为函数的返回值
- 初始化方式
不同点
- 联合的实例所有成员共享相同的存储空间
- 联合的实例大小由最大的成员的类型决定
- 联合初始化实例时初始化的是按照第一个成员的类型来初始化值的
1 | union { |
16.4.1 使用联合来节省空间
原理:在struct
中使用union
作为成员,后者使用struct
作为成员。这种混合的结构可以实现一种数据结构应用于多种情境的效果。
扩展:在c++
中,struct
中的union
可以匿名,在c
中则不得不指定union
的名字。
1 |
|
16.4.2 使用联合来构造混合的数据结构
说明:创建含有不同数据类型的混合数据结构(比如数组)。
1 |
|
16.4.3 为联合添加“标记字段”
用途:为联合提供额外的当前类型信息,防止获取到无意义的值。
1 |
|
16.5 枚举
说明:enum
是一种由程序员列出值的类型,而且程序员必须为每种值(枚举常量)命名。
特点:
- 遵循c语言的作用域规则(如果枚举声明在函数体内,那么它的常量对外部函数是不可见的)
- 声明的方式类似结构和联合
1 | // 最简单的声明方式:定义枚举类型的同时声明枚举变量 |
16.5.1 枚举标记和枚举类型
说明:类似结构和联合的标记,有两种方式。
方式1:enum 标记名 {可能值}
1 | // 创建枚举类型 |
方式2:typedef enum {可能值} 类型名
技巧:利用typedef
来创建布尔类型是非常好的一种方法。
1 | typedef enum {CLUBS, DIAMONDS, HEARTS, SPADES} Suit; |
16.5.2 枚举作为整数
说明:在系统内部,c语言会把枚举变量和常量作为整数处理。
- 当没有为枚举常量指定值时,它的值是一个大于前一个常量的值(默认第一个枚举常量的值为0)
- 可以为枚举常量自由选择不同的值
- 当为枚举常量指定值时,对大小顺序没有要求
- 两个或多个枚举常量具有相同的值也是合法的
1 | enum suit {CLUBS = 20, DIAMONDS = 10, HEARTS, SPADES}; // 20, 10, 11 |
扩展:把整数用作枚举的值是非常危险的,c++
不允许整数用作枚举的值来使用。
1 | int i; |
16.5.3 用枚举声明“标记字段”
说明:enum
和union
配合实现union
的“标记字段”。
优点:
- 不需要额外定义宏
- 明确类型的可能值范围
- 含义更明确
1 | typedeg struct { |